﻿using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
using UnityEngine.Pool;

namespace GameLogic
{
    public class ActivePoolStrategy : MonoBehaviour
    {
        // 池子容量，满容量时触发强制清理 活跃值低于 0.5 * ActiveFactory 的 池子
        [SerializeField]
        public ushort Capacity = 128;

        // 单个池子容量, 满时触发强制清理 清理当前池子 80% 对象
        //[SerializeField]
        //public ushort CapacityOnePool = 256;

        // 默认活跃值
        [SerializeField]
        public float ActiveFactory = 60;//300

        // 采样间隔
        [SerializeField]
        public float SampleSlice = 5;

        // 上一次采样时间
        private float lastSampleTime = 0;
        
        // 活跃池, 记录活跃状态 缓存对象，非活跃的将 定时自动清理。规则: 初始访问对象 活跃值为 300，每隔一秒自动减一，直到0时自动销毁。被访问之后 刷新活跃值
        private readonly Dictionary<string, float> m_ActivePool = new Dictionary<string, float>();

        private void Awake()
        {
            Log.Warning("ActivePoolStrategy Awake");
        }

        public void OnTouch(string path)
        {
            if (!this.m_ActivePool.TryGetValue(path, out _))
            {
                this.m_ActivePool.Add(path, ActiveFactory);
            }
            else
            {
                this.m_ActivePool[path] = ActiveFactory;
            }
        }

        public void OnRecycleEnd(UnityEngine.Pool.IObjectPool<GameObject> pool,string path)
        {
            /*if (pool.CountInactive > CapacityOnePool)
                this.DestroyOutOfOnePoolCapacity(path).Forget();
            else*/
            this.DestroyOutOfCapacity().Forget();
        }
        
        private async UniTask DestroyOutOfCapacity()
        {
            var goPool = GetComponent<GoPool>();
            var pools = goPool.PathPools;
            //var inst2Path = GetComponent<GoPool>().Inst2Path;
            if (pools.Count < Capacity) return;
            await UniTask.NextFrame();
            if (pools.Count < Capacity) return;
            List<string> dirtyPool = ListPool<string>.Get();
            // 清理 活跃值 低于 50% 的池子
            float factory = ActiveFactory * 0.5f;
            foreach (var pool in m_ActivePool)
            {
                if (pool.Value > factory) continue;
                dirtyPool.Add(pool.Key);
            }

            foreach (var path in dirtyPool)
            {
                if (!pools.TryGetValue(path, out var pool)) continue;
                pool.Clear();
                goPool.RemovePool(path);
                this.m_ActivePool.Remove(path);
            }
            ListPool<string>.Release(dirtyPool);
        }

        /*private async UniTask DestroyOutOfOnePoolCapacity(string path)
        {
            await UniTask.NextFrame();
            
            var pools = GetComponent<GoPool>().PathPools;
            if (!pools.TryGetValue(path, out var pool)) return;
            // 池子满了，清理当前池子 80% 对象
            int dirtyCount = Mathf.CeilToInt(pool.CountInactive * 0.8f);
            while (dirtyCount > 0)
            {
                dirtyCount--;
                //Object.Destroy(queue.Dequeue());
                //var inst = queue.Dequeue();
                //_goToLocation.Remove(inst);
                //Object.Destroy(inst);
            }
        }*/

        public void Update()
        {
            var duration = Time.realtimeSinceStartup - this.lastSampleTime;
            if (duration < SampleSlice) return;
            this.lastSampleTime = Time.realtimeSinceStartup;
            this.Sample();
            this.RecycleNonActiveObject();
        }

        // 定时采样活跃池子
        private void Sample()
        {
            if (this.m_ActivePool.Count == 0) return;
            List<string> keys = this.m_ActivePool.Keys.ToList();
            foreach (var path in keys)
                this.m_ActivePool[path] -= SampleSlice;
        }

        private void RecycleNonActiveObject()
        {
            var dirtyPool = ListPool<string>.Get();
            foreach (var pool in this.m_ActivePool)
            {
                if (pool.Value > 0) continue;
                dirtyPool.Add(pool.Key);
            }

            var goPool = GetComponent<GoPool>();
            var pools = goPool.PathPools;
            foreach (var path in dirtyPool)
            {
                if (!pools.TryGetValue(path, out var pool)) continue;
                pool.Clear();
                goPool.RemovePool(path);
                this.m_ActivePool.Remove(path);
            }
            ListPool<string>.Release(dirtyPool);
        }
    }
}